Aquesta és la llibreta on s’integraran tots els apartats que anem treballant durant el curs. Començarem amb una caracterització de la demanda històrica d’un conjunt d’usuaris fictícis d’una comunitat energètica i durant les properes setmanes anirem ampliant les funcionalitats de la llibreta per tal de cobrir tot el que us anem explicant durant el curs.

ATENCIÓ! Tingueu en compte que podreu regenerar els resultats amb els fitxers de dades que vulgueu o necessiteu. Per tant, aquesta eina us podrà ser d’utilitat un cop comenceu a treballar.

library(ggplot2)
library(data.table)
library(lubridate)
library(plotly)
library(dygraphs)
library(tidyr)
library(htmltools)
library(xts)
library(viridis)
library(ggExtra)
library(dutils)
source("funcions.R")

1. Demanda energètica d’una comunitat

Durant aquesta part del curs obtenim les dades de consum d’un seguit d’usuaris a través de la plataforma Datadis. Recordeu que us hem explicat com aconseguir credencials i descarregar informació d’aquesta plataforma en el seguent enllaç al Notion.

La metodología que utilitzarem per a que la llibreta interpreti correctament l’històric de demanda energètica dels usuaris de la comunitat és la seguent:

  1. A través del vostre navegador web preferit, descarregar tots aquells punts de consum (CUPS) que formin part de la comunitat energètica a través de la plataforma Datadis. Descarregarem tant històric de consum com hi hagi disponible. És important que com a mínim tinguem un any complet per cada usuari.
  2. Guardar cada fitxer amb el nom del NIF relacionat, seguit d’un guió baix i un número consecutiu per tal de tenir un nom únic per cada fitxer. Per exemple, en el cas d’un usuari amb NIF 43311022D generariem els seguents fitxers 43311022D_1.csv, 43311022D_2.csv, 43311022D_3.csv …
  3. Moure tots els fitxers descarregats i renombrats a dins de la carpeta Dades/Datadis que podem trobar en el directori de la llibreta.
S'han trobat 11 fitxers amb dades provinents de Datadis.
if(exists_demand_data) {
  
  # Llegim tots els fitxers de dades de Datadis
  df <- read_datadis_files(datadis_files,only_real_values = F)
  df <- df %>% 
    filter(is.finite(time) &
           !duplicated(df[,c("time","NIF","CUPS")]))
}
if(exists_demand_data) {
  df_NIF <- df %>% 
    group_by(time,CUPS) %>%
    summarise(
      consumption=sum(consumption),
      NIF=first(NIF)
    ) %>%
    ungroup() %>%
    select(-CUPS)
}

1.1 Agregació de demanda hora a hora

El potencial autoconsum col·lectiu que es pot fer en una comunitat energètica radica principalment en la complementarietat de les corbes de càrrega individuals. I en la possibilitat, en la mesura del possible, de moure consums en els moments en que l’energia produïda és superior a la demandada, ja que sinó es generen excedents.

Primerament, si tenim en compte l´últim any de dades disponibles per cadascún dels CUPS que integren la comunitat energètica, tenim la seguent corba de càrrega agregada:

if(exists_demand_data) {
  df_ly <- df_NIF %>%
    group_by(ytime= format(time,"%m-%d %H:%M"), NIF) %>%
    summarise(
      consumption=mean(consumption,na.rm=T)
    ) %>%
    mutate(
      ytime = as.POSIXct(paste0("2021-",ytime),format = "%Y-%m-%d %H:%M", 
                         tz="Europe/Madrid")
    ) %>%
    filter( is.finite(ytime) ) %>%
    ungroup()
}

1.2 Agregació de demanda mitja per estació de l’any

Com es pot veure, l’agregació anterior ens permet anar a un detall molt precís sobre quina demanda agregada hi hagués hagut a cada hora i quina repartició entre els diferents usuaris. Tot i això, per entendre una mica més el de esón una mica complicats de dig

if(exists_demand_data) {
  df_ly_season <- df_NIF %>%
    mutate(
      yday = as.numeric(format(time,"%j")),
      season = ifelse(yday<79 | yday>=355, "Hivern",
                      ifelse(yday>=79 & yday<172, "Primavera",
                             ifelse(yday>=172 & yday<266,"Estiu",
                                    "Tardor"))),
      month = month(time)
    ) %>%
    group_by(hour = sprintf("%02i:%02i",hour(time),minute(time)), 
             season, NIF) %>%
    summarise(
      # date = as.Date(paste0(format(time,"%Y-%m"),"-01")),
      date = ifelse(season=="Hivern",as.Date("2021-02-01"),
              ifelse(season=="Primavera",as.Date("2021-05-01"),
               ifelse(season=="Estiu",as.Date("2021-08-01"),
                ifelse(season=="Tardor",as.Date("2021-11-01"))))),
      consumption=mean(consumption)
    ) %>%
    mutate(
      time = as.POSIXct(paste(as.Date(date,origin=as.Date("1970-01-01")),hour))
    ) %>% ungroup() %>%
    select(-hour, -date)
}

Mapa de calor anual de la demanda agregada

Una manera alternativa i que ens proporciona molta informació en un cop de vista al respecte de la demanda energètica agregada són els mapes de calor.

df_ly_total <- df_ly %>% 
  group_by(ytime) %>%
  summarise(
    consumption = mean(consumption)*sum(is.finite(consumption))
  ) %>%
  ungroup() %>% 
  mutate(year = year(ytime),
         month = month(ytime, label=TRUE),
         day = day(ytime),
         hour = hour(ytime)) %>%
  select(day,hour,month,year,consumption) %>%
  fill(consumption)

2. Generació fotovoltàica

# df_pv_scen <- get_pv_timeseries(
#      year = 2021,
#      lat = 41.8,
#      lon = 0.6,
#      database = "PVGIS-SARAH",
#      loss = 14,
#      usehorizon = 1,
#      userhorizon = '0,0,0,0,0,0,5,10,10,5,0,0,0',
#      panels_tbl = data.frame(
#        "varname"=c("a","b"),
#        tilt=c(10,25),
#        azimuth=c(0,0),
#        kWp=c(8,8)
#      )
# )
# plot(df_pv_scen$b)
LS0tCnRpdGxlOiBBbsOgbGlzaSBkZSBkYWRlcyBlbiBjb211bml0YXRzIGVuZXJnw6h0aXF1ZXMKc3VidGl0bGU6IElNTyBTYWx2YWRvciBTZWd1w60gLSBHZXN0acOzIGRlIGNvbXVuaXRhdHMgZW5lcmfDqHRpcXVlcyAtIDIwMjIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKLS0tCgoqKioKCkFxdWVzdGEgw6lzIGxhIGxsaWJyZXRhIG9uIHMnaW50ZWdyYXJhbiB0b3RzIGVscyBhcGFydGF0cyBxdWUgYW5lbSB0cmViYWxsYW50IGR1cmFudCBlbCBjdXJzLiBDb21lbsOnYXJlbSBhbWIgdW5hIGNhcmFjdGVyaXR6YWNpw7MgZGUgbGEgZGVtYW5kYSBoaXN0w7JyaWNhIGQndW4gY29uanVudCBkJ3VzdWFyaXMgZmljdMOtY2lzIGQndW5hIGNvbXVuaXRhdCBlbmVyZ8OodGljYSBpIGR1cmFudCBsZXMgcHJvcGVyZXMgc2V0bWFuZXMgYW5pcmVtIGFtcGxpYW50IGxlcyBmdW5jaW9uYWxpdGF0cyBkZSBsYSBsbGlicmV0YSBwZXIgdGFsIGRlIGNvYnJpciB0b3QgZWwgcXVlIHVzIGFuZW0gZXhwbGljYW50IGR1cmFudCBlbCBjdXJzLiAKCioqQVRFTkNJw5MhKiogVGluZ3VldSBlbiBjb21wdGUgcXVlIHBvZHJldSByZWdlbmVyYXIgZWxzIHJlc3VsdGF0cyBhbWIgZWxzIGZpdHhlcnMgZGUgZGFkZXMgcXVlIHZ1bGd1ZXUgbyBuZWNlc3NpdGV1LiBQZXIgdGFudCwgYXF1ZXN0YSBlaW5hIHVzIHBvZHLDoCBzZXIgZCd1dGlsaXRhdCB1biBjb3AgY29tZW5jZXUgYSB0cmViYWxsYXIuIAoKYGBge3IsIGVjaG89VCwgcmVzdWx0cz1GLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShkeWdyYXBocykKbGlicmFyeSh0aWR5cikKbGlicmFyeShodG1sdG9vbHMpCmxpYnJhcnkoeHRzKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoZ2dFeHRyYSkKbGlicmFyeShkdXRpbHMpCnNvdXJjZSgiZnVuY2lvbnMuUiIpCmBgYAoqKioKCiMgMS4gRGVtYW5kYSBlbmVyZ8OodGljYSBkJ3VuYSBjb211bml0YXQKCkR1cmFudCBhcXVlc3RhIHBhcnQgZGVsIGN1cnMgb2J0ZW5pbSBsZXMgZGFkZXMgZGUgY29uc3VtIGQndW4gc2VndWl0IGQndXN1YXJpcyBhIHRyYXbDqXMgZGUgbGEgcGxhdGFmb3JtYSBbRGF0YWRpc10oaHR0cHM6Oi8vd3d3LmRhdGFkaXMuZXMpLiBSZWNvcmRldSBxdWUgdXMgaGVtIGV4cGxpY2F0IGNvbSBhY29uc2VndWlyIGNyZWRlbmNpYWxzIGkgZGVzY2FycmVnYXIgaW5mb3JtYWNpw7MgZCdhcXVlc3RhIHBsYXRhZm9ybWEgZW4gZWwgc2VndWVudCBbZW5sbGHDpyBhbCBOb3Rpb25dKGh0dHBzOi8vc3VtcHR1b3VzLXB1cHB5LWI4Ni5ub3Rpb24uc2l0ZS9PYnRlbmlyLWNyZWRlbmNpYWxzLWktZGFkZXMtZGUtRGF0YWRpcy0xYzg3NTE2YzExZjc0MGRmOGI3NzVhYWJhODhkYjU2ZSkuCgpMYSBtZXRvZG9sb2fDrWEgcXVlIHV0aWxpdHphcmVtIHBlciBhIHF1ZSBsYSBsbGlicmV0YSBpbnRlcnByZXRpIGNvcnJlY3RhbWVudCBsJ2hpc3TDsnJpYyBkZSBkZW1hbmRhIGVuZXJnw6h0aWNhIGRlbHMgdXN1YXJpcyBkZSBsYSBjb211bml0YXQgw6lzIGxhIHNlZ3VlbnQ6CgoxLiBBIHRyYXbDqXMgZGVsIHZvc3RyZSBuYXZlZ2Fkb3Igd2ViIHByZWZlcml0LCBkZXNjYXJyZWdhciB0b3RzIGFxdWVsbHMgcHVudHMgZGUgY29uc3VtIChDVVBTKSBxdWUgZm9ybWluIHBhcnQgZGUgbGEgY29tdW5pdGF0IGVuZXJnw6h0aWNhIGEgdHJhdsOpcyBkZSBsYSBwbGF0YWZvcm1hIERhdGFkaXMuIERlc2NhcnJlZ2FyZW0gdGFudCBoaXN0w7JyaWMgZGUgY29uc3VtIGNvbSBoaSBoYWdpIGRpc3BvbmlibGUuIMOJcyBpbXBvcnRhbnQgcXVlIGNvbSBhIG3DrW5pbSB0aW5ndWVtIHVuIGFueSBjb21wbGV0IHBlciBjYWRhIHVzdWFyaS4KMi4gR3VhcmRhciBjYWRhIGZpdHhlciBhbWIgZWwgbm9tIGRlbCBOSUYgcmVsYWNpb25hdCwgc2VndWl0IGQndW4gZ3Vpw7MgYmFpeCBpIHVuIG7Dum1lcm8gY29uc2VjdXRpdSBwZXIgdGFsIGRlIHRlbmlyIHVuIG5vbSDDum5pYyBwZXIgY2FkYSBmaXR4ZXIuIFBlciBleGVtcGxlLCBlbiBlbCBjYXMgZCd1biB1c3VhcmkgYW1iIE5JRiA0MzMxMTAyMkQgZ2VuZXJhcmllbSBlbHMgc2VndWVudHMgZml0eGVycyA0MzMxMTAyMkRfMS5jc3YsIDQzMzExMDIyRF8yLmNzdiwgNDMzMTEwMjJEXzMuY3N2IC4uLgozLiBNb3VyZSB0b3RzIGVscyBmaXR4ZXJzIGRlc2NhcnJlZ2F0cyBpIHJlbm9tYnJhdHMgYSBkaW5zIGRlIGxhIGNhcnBldGEgRGFkZXMvRGF0YWRpcyBxdWUgcG9kZW0gdHJvYmFyIGVuIGVsIGRpcmVjdG9yaSBkZSBsYSBsbGlicmV0YS4KCgpgYGB7ciwgZWNobz1GLCByZXN1bHRzPUZ9CndkIDwtIG5vcm1hbGl6ZVBhdGgoIi4iKQpkaXIuY3JlYXRlKHBhc3RlMCh3ZCwiLy4uL0RhZGVzIiksIHNob3dXYXJuaW5ncyA9IEYpCmRpci5jcmVhdGUocGFzdGUwKHdkLCIvLi4vRGFkZXMvRGF0YWRpcyIpLHNob3dXYXJuaW5ncyA9IEYpCgpkYXRhZGlzX2ZpbGVzIDwtIGxpc3QuZmlsZXMocGFzdGUwKHdkLCIvLi4vRGFkZXMvRGF0YWRpcyIpLGZ1bGwubmFtZXMgPSBUKQpleGlzdHNfZGVtYW5kX2RhdGEgPC0gbGVuZ3RoKGRhdGFkaXNfZmlsZXMpID4gMAoKaWYoIWV4aXN0c19kZW1hbmRfZGF0YSkgewogIG1lc3NhZ2UoIkFjdHVhbG1lbnQgbm8gZXhpc3RlaXhlbiBmaXR4ZXJzIGRlIGRlbWFuZGEgZW5lcmfDqHRpY2EuIAogICAgICAgICAgU2kgdXMgcGxhdSwgYWZlZ2l1LW4naGkgaSBleGVjdXRldSBsYSBsbGlicmV0YSBkZSBub3UuIikKfSBlbHNlIHsKICBtZXNzYWdlKHNwcmludGYoIlMnaGFuIHRyb2JhdCAlcyBmaXR4ZXJzIGFtYiBkYWRlcyBwcm92aW5lbnRzIGRlIERhdGFkaXMuIiwKICAgICAgICAgICAgICAgICAgbGVuZ3RoKGRhdGFkaXNfZmlsZXMpKSkKfQpgYGAKCmBgYHtyLCBlY2hvPVQsIHJlc3VsdHM9RiwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CmlmKGV4aXN0c19kZW1hbmRfZGF0YSkgewogIAogICMgTGxlZ2ltIHRvdHMgZWxzIGZpdHhlcnMgZGUgZGFkZXMgZGUgRGF0YWRpcwogIGRmIDwtIHJlYWRfZGF0YWRpc19maWxlcyhkYXRhZGlzX2ZpbGVzLG9ubHlfcmVhbF92YWx1ZXMgPSBGKQogIGRmIDwtIGRmICU+JSAKICAgIGZpbHRlcihpcy5maW5pdGUodGltZSkgJgogICAgICAgICAgICFkdXBsaWNhdGVkKGRmWyxjKCJ0aW1lIiwiTklGIiwiQ1VQUyIpXSkpCn0KYGBgCgo8Y2VudGVyPgpgYGB7ciwgZWNobz1GLCByZXN1bHRzPVQsIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBvdXQud2lkdGg9IjEwMCUifQppZihleGlzdHNfZGVtYW5kX2RhdGEpIHsKICAjIFZpc3VhbGl0emVtIGxlcyBjb3JiZXMgZGUgY8OgcnJlZ2EgaW5kaXZpZHVhbG1lbnQgcGVyIE5JRiwgdXRpbGl0emFudCB1biBncsOgZmljIGludGVyYWN0aXUKICAjIGdncGxvdGx5KAogICMgICBnZ3Bsb3QoZGYpICsgCiAgIyAgICAgZ2VvbV9saW5lKGFlcyh0aW1lLGNvbnN1bXB0aW9uLGdyb3VwPUNVUFMsY29sPU5JRiksYWxwaGE9MC42KSArCiAgIyAgICAgeGxhYigidGVtcHMiKSArIHlsYWIoImNvbnN1bSAoa1doKSIpICsgCiAgIyAgICAgZ2d0aXRsZSgiQ29yYmVzIGRlIGPDoHJyZWdhIGluZGl2aWR1YWxzIHBlciBOSUYiKSArIHRoZW1lX2J3KCkKICAjICkKICBwdHMgPC0gbGFwcGx5KHVuaXF1ZShkZiRDVVBTKSxmdW5jdGlvbihpKXsKICAgIGF1eCA8LSBkZiAlPiUgZmlsdGVyKENVUFM9PWkpCiAgICBwIDwtIGR5Z3JhcGgoeHRzKHggPSBkYXRhLmZyYW1lKHZhbG9yPWF1eCRjb25zdW1wdGlvbiksIAogICAgICAgICAgICAgICAgb3JkZXIuYnkgPSBhdXgkdGltZSksIAogICAgICAgICAgICBtYWluPXNwcmludGYoIk5JRjogJXMsIENVUFM6ICVzIixkZiROSUZbZGYkQ1VQUz09aV1bMV0saSksCiAgICAgICAgICAgIHdpZHRoID0gODAwLCBoZWlnaHQ9MzUwKSAlPiUKICAgICAgZHlPcHRpb25zKGNvbm5lY3RTZXBhcmF0ZWRQb2ludHMgPSBULCBmaWxsR3JhcGg9VCwgZmlsbEFscGhhPTAuNCwgCiAgICAgICAgICAgICAgICBkcmF3R3JpZCA9IEYsIGNvbG9ycz0iI0Q4QUU1QSIpICAlPiUKICAgICAgZHlSYW5nZVNlbGVjdG9yKGhlaWdodCA9IDMwKSAlPiUKICAgICAgZHlBeGlzKCJ4IiwgbGFiZWwgPSAidGVtcHMiKSAlPiUKICAgICAgZHlBeGlzKCJ5IiwgbGFiZWwgPSAiY29uc3VtIChrV2gpIikgJT4lCiAgICBkeUhpZ2hsaWdodChoaWdobGlnaHRDaXJjbGVTaXplID0gMi41LAogICAgICAgICAgICAgICAgaGlnaGxpZ2h0U2VyaWVzQmFja2dyb3VuZEFscGhhID0gMC44LAogICAgICAgICAgICAgICAgaGlkZU9uTW91c2VPdXQgPSBUKSAlPiUKICAgIGR5Um9sbGVyKHJvbGxQZXJpb2QgPSAxKSAlPiUgCiAgICBkeUNyb3NzaGFpcihkaXJlY3Rpb24gPSAidmVydGljYWwiKQogICAgIGh0bWx0b29sczo6dGFncyRkaXYocCwgc3R5bGUgPSAicGFkZGluZzoxMHB4OyBib3JkZXI6IHNvbGlkIikKICB9KQogIGh0bWx0b29sczo6dGFnTGlzdChwdHMpCn0KYGBgCjwvY2VudGVyPgoKYGBge3IsIGVjaG89VCwgcmVzdWx0cz1GLCB3YXJuaW5nPUYsIG1lc3NhZ2U9Rn0KaWYoZXhpc3RzX2RlbWFuZF9kYXRhKSB7CiAgZGZfTklGIDwtIGRmICU+JSAKICAgIGdyb3VwX2J5KHRpbWUsQ1VQUykgJT4lCiAgICBzdW1tYXJpc2UoCiAgICAgIGNvbnN1bXB0aW9uPXN1bShjb25zdW1wdGlvbiksCiAgICAgIE5JRj1maXJzdChOSUYpCiAgICApICU+JQogICAgdW5ncm91cCgpICU+JQogICAgc2VsZWN0KC1DVVBTKQp9CmBgYAoKCioqKgoKIyMjIDEuMSBBZ3JlZ2FjacOzIGRlIGRlbWFuZGEgaG9yYSBhIGhvcmEKCgpFbCBwb3RlbmNpYWwgYXV0b2NvbnN1bSBjb2zCt2xlY3RpdSBxdWUgZXMgcG90IGZlciBlbiB1bmEgY29tdW5pdGF0IGVuZXJnw6h0aWNhIHJhZGljYSBwcmluY2lwYWxtZW50IGVuIGxhIGNvbXBsZW1lbnRhcmlldGF0IGRlIGxlcyBjb3JiZXMgZGUgY8OgcnJlZ2EgaW5kaXZpZHVhbHMuIEkgZW4gbGEgcG9zc2liaWxpdGF0LCBlbiBsYSBtZXN1cmEgZGVsIHBvc3NpYmxlLCBkZSBtb3VyZSBjb25zdW1zIGVuIGVscyBtb21lbnRzIGVuIHF1ZSBsJ2VuZXJnaWEgcHJvZHXDr2RhIMOpcyBzdXBlcmlvciBhIGxhIGRlbWFuZGFkYSwgamEgcXVlIHNpbsOzIGVzIGdlbmVyZW4gZXhjZWRlbnRzLgoKUHJpbWVyYW1lbnQsIHNpIHRlbmltIGVuIGNvbXB0ZSBswrTDumx0aW0gYW55IGRlIGRhZGVzIGRpc3BvbmlibGVzIHBlciBjYWRhc2PDum4gZGVscyBDVVBTIHF1ZSBpbnRlZ3JlbiBsYSBjb211bml0YXQgZW5lcmfDqHRpY2EsIHRlbmltIGxhIHNlZ3VlbnQgY29yYmEgZGUgY8OgcnJlZ2EgYWdyZWdhZGE6CgpgYGB7ciwgZWNobz1ULCByZXN1bHRzPUYsIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQppZihleGlzdHNfZGVtYW5kX2RhdGEpIHsKICBkZl9seSA8LSBkZl9OSUYgJT4lCiAgICBncm91cF9ieSh5dGltZT0gZm9ybWF0KHRpbWUsIiVtLSVkICVIOiVNIiksIE5JRikgJT4lCiAgICBzdW1tYXJpc2UoCiAgICAgIGNvbnN1bXB0aW9uPW1lYW4oY29uc3VtcHRpb24sbmEucm09VCkKICAgICkgJT4lCiAgICBtdXRhdGUoCiAgICAgIHl0aW1lID0gYXMuUE9TSVhjdChwYXN0ZTAoIjIwMjEtIix5dGltZSksZm9ybWF0ID0gIiVZLSVtLSVkICVIOiVNIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICB0ej0iRXVyb3BlL01hZHJpZCIpCiAgICApICU+JQogICAgZmlsdGVyKCBpcy5maW5pdGUoeXRpbWUpICkgJT4lCiAgICB1bmdyb3VwKCkKfQpgYGAKCjxjZW50ZXI+IApgYGB7ciwgZWNobz1GLCByZXN1bHRzPVQsIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQppZihleGlzdHNfZGVtYW5kX2RhdGEpIHsKICAjIFZpc3VhbGl0emVtIGxlcyBjb3JiZXMgZGUgY8OgcnJlZ2EgYWdyZWdhZGVzIGQndW4gYW55IHRpcHVzIHBlciBOSUYsIHV0aWxpdHphbnQgdW4gZ3LDoGZpYyBpbnRlcmFjdGl1CiAgIyBnZ3Bsb3RseSgKICAjICAgZ2dwbG90KGRmX2x5KSArIAogICMgICAgIGdlb21fYXJlYShhZXMoeXRpbWUsY29uc3VtcHRpb24sZmlsbD1OSUYsZ3JvdXA9Q1VQUykscG9zaXRpb249InN0YWNrIikgKwogICMgICAgIHhsYWIoInRlbXBzIikgKyB5bGFiKCJjb25zdW0gKGtXaCkiKSArIAogICMgICAgIGdndGl0bGUoIkNvcmJlcyBkZSBjw6BycmVnYSBhZ3JlZ2FkZXMgZHVyYW50IHVuIGFueSIpICsgdGhlbWVfYncoKQogICMgKQogIGRmX2x5X2Nhc3QgPC0gcGl2b3Rfd2lkZXIoCiAgICBkYXRhID0gZGZfbHksIAogICAgbmFtZXNfZnJvbSA9ICJOSUYiLCAKICAgIHZhbHVlc19mcm9tID0gImNvbnN1bXB0aW9uIikKICBwIDwtIGR5Z3JhcGgoCiAgICAgIHh0cyhkZl9seV9jYXN0ICU+JSBzZWxlY3QoLXl0aW1lKSwgb3JkZXIuYnkgPSBkZl9seV9jYXN0JHl0aW1lKSwKICAgICAgbWFpbiA9ICJDb3JiZXMgZGUgY8OgcnJlZ2EgYWdyZWdhZGVzIHBlciBOSUYgZHVyYW50IHVuIGFueSBuYXR1cmFsIiwKICAgICAgd2lkdGggPSA5MDAsCiAgICAgIGhlaWdodCA9IDUwMAogICAgKSAlPiUKICAgIGR5T3B0aW9ucyhzdGFja2VkR3JhcGggPSBUUlVFLCBjb25uZWN0U2VwYXJhdGVkUG9pbnRzID0gVCwKICAgICAgICAgICAgICBmaWxsR3JhcGg9VCwgZHJhd0dyaWQgPSBGKSAgJT4lCiAgICBkeUxlZ2VuZChzaG93ID0gImFsd2F5cyIpICU+JQogICAgZHlSYW5nZVNlbGVjdG9yKGhlaWdodCA9IDMwKSAlPiUKICAgIGR5QXhpcygieCIsIGxhYmVsID0gInRlbXBzIikgJT4lCiAgICBkeUF4aXMoInkiLCBsYWJlbCA9ICJjb25zdW0gKGtXaCkiKSAlPiUKICAgIGR5SGlnaGxpZ2h0KGhpZ2hsaWdodENpcmNsZVNpemUgPSAyLjUsCiAgICAgICAgICAgICAgICBoaWdobGlnaHRTZXJpZXNCYWNrZ3JvdW5kQWxwaGEgPSAwLjQsCiAgICAgICAgICAgICAgICBoaWRlT25Nb3VzZU91dCA9IFQpICU+JQogICAgZHlSb2xsZXIocm9sbFBlcmlvZCA9IDEpICU+JSAKICAgIGR5Q3Jvc3NoYWlyKGRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIpCiAgaHRtbHRvb2xzOjp0YWdMaXN0KAogICAgbGlzdChodG1sdG9vbHM6OnRhZ3MkZGl2KHAsIHN0eWxlID0gInBhZGRpbmc6MTBweDsgYm9yZGVyOiBzb2xpZCIpKQogICkKfQpgYGAKPC9jZW50ZXI+CgoqKioKCiMjIyAxLjIgQWdyZWdhY2nDsyBkZSBkZW1hbmRhIG1pdGphIHBlciBlc3RhY2nDsyBkZSBsJ2FueQoKQ29tIGVzIHBvdCB2ZXVyZSwgbCdhZ3JlZ2FjacOzIGFudGVyaW9yIGVucyBwZXJtZXQgYW5hciBhIHVuIGRldGFsbCBtb2x0IHByZWPDrXMgc29icmUgcXVpbmEgZGVtYW5kYSBhZ3JlZ2FkYSBoaSBoYWd1w6lzIGhhZ3V0IGEgY2FkYSBob3JhIGkgcXVpbmEgcmVwYXJ0aWNpw7MgZW50cmUgZWxzIGRpZmVyZW50cyB1c3VhcmlzLiAKVG90IGkgYWl4w7IsIHBlciBlbnRlbmRyZSB1bmEgbWljYSBtw6lzIGVsIGRlIGVzw7NuIHVuYSBtaWNhIGNvbXBsaWNhdHMgZGUgZGlnCgoKYGBge3IsIGVjaG89VCwgcmVzdWx0cz1GLCB3YXJuaW5nPUYsIG1lc3NhZ2U9Rn0KaWYoZXhpc3RzX2RlbWFuZF9kYXRhKSB7CiAgZGZfbHlfc2Vhc29uIDwtIGRmX05JRiAlPiUKICAgIG11dGF0ZSgKICAgICAgeWRheSA9IGFzLm51bWVyaWMoZm9ybWF0KHRpbWUsIiVqIikpLAogICAgICBzZWFzb24gPSBpZmVsc2UoeWRheTw3OSB8IHlkYXk+PTM1NSwgIkhpdmVybiIsCiAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoeWRheT49NzkgJiB5ZGF5PDE3MiwgIlByaW1hdmVyYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHlkYXk+PTE3MiAmIHlkYXk8MjY2LCJFc3RpdSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUYXJkb3IiKSkpLAogICAgICBtb250aCA9IG1vbnRoKHRpbWUpCiAgICApICU+JQogICAgZ3JvdXBfYnkoaG91ciA9IHNwcmludGYoIiUwMmk6JTAyaSIsaG91cih0aW1lKSxtaW51dGUodGltZSkpLCAKICAgICAgICAgICAgIHNlYXNvbiwgTklGKSAlPiUKICAgIHN1bW1hcmlzZSgKICAgICAgIyBkYXRlID0gYXMuRGF0ZShwYXN0ZTAoZm9ybWF0KHRpbWUsIiVZLSVtIiksIi0wMSIpKSwKICAgICAgZGF0ZSA9IGlmZWxzZShzZWFzb249PSJIaXZlcm4iLGFzLkRhdGUoIjIwMjEtMDItMDEiKSwKICAgICAgICAgICAgICBpZmVsc2Uoc2Vhc29uPT0iUHJpbWF2ZXJhIixhcy5EYXRlKCIyMDIxLTA1LTAxIiksCiAgICAgICAgICAgICAgIGlmZWxzZShzZWFzb249PSJFc3RpdSIsYXMuRGF0ZSgiMjAyMS0wOC0wMSIpLAogICAgICAgICAgICAgICAgaWZlbHNlKHNlYXNvbj09IlRhcmRvciIsYXMuRGF0ZSgiMjAyMS0xMS0wMSIpKSkpKSwKICAgICAgY29uc3VtcHRpb249bWVhbihjb25zdW1wdGlvbikKICAgICkgJT4lCiAgICBtdXRhdGUoCiAgICAgIHRpbWUgPSBhcy5QT1NJWGN0KHBhc3RlKGFzLkRhdGUoZGF0ZSxvcmlnaW49YXMuRGF0ZSgiMTk3MC0wMS0wMSIpKSxob3VyKSkKICAgICkgJT4lIHVuZ3JvdXAoKSAlPiUKICAgIHNlbGVjdCgtaG91ciwgLWRhdGUpCn0KYGBgCgo8Y2VudGVyPiAKYGBge3IsIGVjaG89RiwgcmVzdWx0cz1ULCB3YXJuaW5nPUYsIG1lc3NhZ2U9Rn0KaWYoZXhpc3RzX2RlbWFuZF9kYXRhKSB7CiAgZGZfbHlfY2FzdF9zZWFzb24gPC0gYXMuZGF0YS5mcmFtZSgKICAgIHBpdm90X3dpZGVyKGRmX2x5X3NlYXNvbiwgCiAgICAgIG5hbWVzX2Zyb20gPSAiTklGIiwgCiAgICAgIHZhbHVlc19mcm9tID0gImNvbnN1bXB0aW9uIix2YWx1ZXNfZm4gPSBtZWFuKSkKICBwdHMgPC0gbGFwcGx5KHVuaXF1ZShkZl9seV9jYXN0X3NlYXNvbiRzZWFzb24pLCBmdW5jdGlvbih3cyl7CiAgICBhdXggPC0gZGZfbHlfY2FzdF9zZWFzb24gJT4lIGZpbHRlcihzZWFzb249PXdzKQogICAgcCA8LSBkeWdyYXBoKAogICAgICAgIHh0cyhhdXggJT4lIHNlbGVjdCgtdGltZSwtc2Vhc29uKSwgb3JkZXIuYnkgPSBhdXgkdGltZSksCiAgICAgICAgbWFpbiA9IHNwcmludGYoIiVzIC0gQ29yYmVzIGRlIGPDoHJyZWdhIG1pdGplcyBhZ3JlZ2FkZXMgcGVyIE5JRiIsd3MpLAogICAgICAgIHdpZHRoID0gOTAwLAogICAgICAgIGhlaWdodCA9IDUwMAogICAgICApICU+JQogICAgICBkeU9wdGlvbnMoc3RhY2tlZEdyYXBoID0gVFJVRSwgY29ubmVjdFNlcGFyYXRlZFBvaW50cyA9IFQsCiAgICAgICAgICAgICAgICBmaWxsR3JhcGg9VCwgZHJhd0dyaWQgPSBGKSAgJT4lCiAgICAgIGR5TGVnZW5kKHNob3cgPSAiYWx3YXlzIikgJT4lCiAgICAgIGR5UmFuZ2VTZWxlY3RvcihoZWlnaHQgPSAzMCkgJT4lCiAgICAgIGR5QXhpcygieCIsIGxhYmVsID0gInRlbXBzIikgJT4lCiAgICAgIGR5QXhpcygieSIsIGxhYmVsID0gImNvbnN1bSAoa1doKSIpICU+JQogICAgICBkeUhpZ2hsaWdodChoaWdobGlnaHRDaXJjbGVTaXplID0gMi41LAogICAgICAgICAgICAgICAgICBoaWdobGlnaHRTZXJpZXNCYWNrZ3JvdW5kQWxwaGEgPSAwLjQsCiAgICAgICAgICAgICAgICAgIGhpZGVPbk1vdXNlT3V0ID0gVCkgJT4lCiAgICAgIGR5Um9sbGVyKHJvbGxQZXJpb2QgPSAxKSAlPiUgCiAgICAgIGR5Q3Jvc3NoYWlyKGRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIpCiAgICAgIGh0bWx0b29sczo6dGFncyRkaXYocCwgc3R5bGUgPSAicGFkZGluZzoxMHB4OyBib3JkZXI6IHNvbGlkIikKICB9KQogIGh0bWx0b29sczo6dGFnTGlzdChwdHMpCn0KYGBgCjwvY2VudGVyPgoKKioqCgojIyMgTWFwYSBkZSBjYWxvciBhbnVhbCBkZSBsYSBkZW1hbmRhIGFncmVnYWRhCgpVbmEgbWFuZXJhIGFsdGVybmF0aXZhIGkgcXVlIGVucyBwcm9wb3JjaW9uYSBtb2x0YSBpbmZvcm1hY2nDsyBlbiB1biBjb3AgZGUgdmlzdGEgYWwgcmVzcGVjdGUgZGUgbGEgZGVtYW5kYSBlbmVyZ8OodGljYSBhZ3JlZ2FkYSBzw7NuIGVscyBtYXBlcyBkZSBjYWxvci4KCmBgYHtyfQpkZl9seV90b3RhbCA8LSBkZl9seSAlPiUgCiAgZ3JvdXBfYnkoeXRpbWUpICU+JQogIHN1bW1hcmlzZSgKICAgIGNvbnN1bXB0aW9uID0gbWVhbihjb25zdW1wdGlvbikqc3VtKGlzLmZpbml0ZShjb25zdW1wdGlvbikpCiAgKSAlPiUKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZSh5ZWFyID0geWVhcih5dGltZSksCiAgICAgICAgIG1vbnRoID0gbW9udGgoeXRpbWUsIGxhYmVsPVRSVUUpLAogICAgICAgICBkYXkgPSBkYXkoeXRpbWUpLAogICAgICAgICBob3VyID0gaG91cih5dGltZSkpICU+JQogIHNlbGVjdChkYXksaG91cixtb250aCx5ZWFyLGNvbnN1bXB0aW9uKSAlPiUKICBmaWxsKGNvbnN1bXB0aW9uKQpgYGAKCjxjZW50ZXI+CmBgYHtyLGVjaG89RixyZXN1bHRzPVQsbWVzc2FnZT1GLHdhcm5pbmc9RixvdXQud2lkdGg9JzEwMCUnfQpwIDwtZ2dwbG90KGRmX2x5X3RvdGFsLGFlcyhkYXksaG91cixmaWxsPWNvbnN1bXB0aW9uKSkrCiAgZ2VvbV90aWxlKGNvbG9yPSAid2hpdGUiLHNpemU9MC4xKSArIAogIHNjYWxlX2ZpbGxfdmlyaWRpcyhuYW1lPSJjb25zdW0gKGtXaCkiLG9wdGlvbiA9IkMiKQpwIDwtcCArIGZhY2V0X2dyaWQoeWVhcn5tb250aCkKcCA8LXAgKyBzY2FsZV95X2NvbnRpbnVvdXModHJhbnMgPSAicmV2ZXJzZSIsIGJyZWFrcyA9IHVuaXF1ZShkZl9seV90b3RhbCRob3VyKSkKcCA8LXAgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID1jKDEsMTAsMjAsMzEpKQpwIDwtcCArIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gOCkKcCA8LXAgKyBsYWJzKHRpdGxlPSBwYXN0ZSgiTWFwYSBkZSBjYWxvciBkZSBsYSBkZW1hbmRhIGFncmVnYWRhIGVuIGFueSB0aXB1cyIpLCB4PSJEaWEiLCB5PSJIb3JhIikKcCA8LXAgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikrCiAgdGhlbWUocGxvdC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZSA9IDE0KSkrCiAgdGhlbWUoYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9NikpICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT02LGFuZ2xlPTkwKSkgKwogIHRoZW1lKHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3VyPSJ3aGl0ZSIpKSsKICB0aGVtZShwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChoanVzdD0wKSkrCiAgdGhlbWUoYXhpcy50aWNrcz1lbGVtZW50X2JsYW5rKCkpKwogIHRoZW1lKGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT03KSkrCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpKSsKICB0aGVtZShsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT02KSkrCiAgcmVtb3ZlR3JpZCgpI2dnRXh0cmEKcApgYGAKPC9jZW50ZXI+CgojIyAyLiBHZW5lcmFjacOzIGZvdG92b2x0w6BpY2EKCmBgYHtyfQojIGRmX3B2X3NjZW4gPC0gZ2V0X3B2X3RpbWVzZXJpZXMoCiMgICAgICB5ZWFyID0gMjAyMSwKIyAgICAgIGxhdCA9IDQxLjgsCiMgICAgICBsb24gPSAwLjYsCiMgICAgICBkYXRhYmFzZSA9ICJQVkdJUy1TQVJBSCIsCiMgICAgICBsb3NzID0gMTQsCiMgICAgICB1c2Vob3Jpem9uID0gMSwKIyAgICAgIHVzZXJob3Jpem9uID0gJzAsMCwwLDAsMCwwLDUsMTAsMTAsNSwwLDAsMCcsCiMgICAgICBwYW5lbHNfdGJsID0gZGF0YS5mcmFtZSgKIyAgICAgICAgInZhcm5hbWUiPWMoImEiLCJiIiksCiMgICAgICAgIHRpbHQ9YygxMCwyNSksCiMgICAgICAgIGF6aW11dGg9YygwLDApLAojICAgICAgICBrV3A9Yyg4LDgpCiMgICAgICApCiMgKQojIHBsb3QoZGZfcHZfc2NlbiRiKQpgYGAK